package zdream.pmfield.stage.director;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import zdream.pmfield.stage.buff.StageBuff;

/**
 * @author Zdream
 * @date 2023-11-23
 * @since 0.0.1
 */
public class HookStore {
	protected final Map<HookType, List<HookHandler>> handlers = new HashMap<>();
	public int id = 0;

	public Supplier<Boolean> registerHook(HookType hookType, HookHandler wrapper) {
		handlers.compute(hookType, ((hookType1, hookHandlers) -> {
				if (hookHandlers == null) {
					hookHandlers = new ArrayList<>();
				}
				hookHandlers.add(wrapper);
				return hookHandlers;
		}));

		return () -> Optional.ofNullable(handlers.get(hookType))
				.map(list -> list.remove(wrapper))
				.orElse(false);
	}

	public HookHandlerBuilder hookBuilder() {
		final HookHandlerBuilder builder = new HookHandlerBuilder(++id);
		builder.saveHandler = b -> registerHook(b.hookType, b.build());
		return builder;
	}

	void fill(Hooks.HookContext context) {
		final List<HookHandler> list = handlers.get(context.hookType);
		if (list == null) {
			return;
		}

		final List<HookHandler> copied = context.handlers;
		copied.addAll(list);
	}

	public static class HookHandler implements Comparable<HookHandler> {
		public final Consumer<Hooks.HookContext> handler;
		public final int id;
		public final StageBuff<?> owner;
		public final int priority;

		private HookHandler(Consumer<Hooks.HookContext> handler, int id, StageBuff<?> owner, int priority) {
			this.handler = Objects.requireNonNull(handler);
			this.id = id;
			this.owner = Objects.requireNonNull(owner);
			this.priority = priority;
		}

		@Override
		public int compareTo(HookHandler o) {
			if (this.priority == o.priority) {
				return this.id - o.id;
			}
			return this.priority - o.priority;
		}
	}

	public static class HookHandlerBuilder {
		public final int id;
		private Consumer<Hooks.HookContext> handler;
		private StageBuff<?> owner;
		private HookType hookType;
		private int priority;
		private Function<HookHandlerBuilder, Supplier<Boolean>> saveHandler;
		private Consumer<Supplier<Boolean>> cleaner;

		public HookHandlerBuilder(int id) {
			this.id = id;
		}

		public HookHandlerBuilder handle(Consumer<Hooks.HookContext> handler) {
			this.handler = handler;
			return this;
		}

		public HookHandlerBuilder withOwner(StageBuff<?> owner) {
			this.owner = owner;
			return this;
		}

		public HookHandlerBuilder withType(HookType hookType) {
			this.hookType = hookType;
			return this;
		}

		/**
		 * 优先级, 低的优先执行. 默认 0.
		 * 测 buff 执行的先后顺序交给 case.
		 */
		public HookHandlerBuilder withPriority(int priority) {
			this.priority = priority;
			return this;
		}

		public HookHandlerBuilder withCleaner(Consumer<Supplier<Boolean>> cleaner) {
			Objects.requireNonNull(cleaner);
			this.cleaner = (this.cleaner == null) ? cleaner : this.cleaner.andThen(cleaner);
			return this;
		}

		private HookHandler build() {
			return new HookHandler(this.handler, id, owner, priority);
		}

		public Supplier<Boolean> buildAndSave() {
			final Supplier<Boolean> cleaner = this.saveHandler.apply(this);
			if (this.cleaner != null) {
				this.cleaner.accept(cleaner);
			}
			return cleaner;
		}
	}
}
